/*
 * hash.c
 *
 * Copyright (C) 2011 - Dr.NP
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @version hash.c 1.0.0
 * @package libbspcore
 * @author Dr.NP <np@bsgroup.org>
 * @update 03/18/2011
 */

/**
 * Hash algorisim
 *
 * === CHANGELOG ==
 * [03/18/2011] - Creation
 */

#include "bsp.h"

#include "bsp_hash.h"

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

inline u_int32_t hash(const char *key, size_t len)
{
    if (!key)
    {
        return 0;
    }

    if ((signed) len <= 0)
    {
        len = strlen(key);
    }
    
	u_int32_t a, b, c;
	u_int32_t *k = (u_int32_t *) key;
	a = (u_int32_t) 0xdeadface + (u_int32_t) len;
	b = (u_int32_t) 0xfacebeef + (u_int32_t) len;
	c = (u_int32_t) 0xbeefdead + (u_int32_t) len;

	// Pass every 12 bytes
	while (len > 12)
	{
		a += k[0];
		b += k[1];
		c += k[2];

		mix(a, b, c);
			
		len -= 12;
		k += 3;
	}

	mix(a, b, c);

	// Mix data
	switch (len)
    {
        case 12: c += k[2]; b += k[1]; a += k[0]; break;
        case 11: c += k[2] & 0xffffff; b += k[1]; a += k[0]; break;
        case 10: c += k[2] & 0xffff; b += k[1]; a += k[0]; break;
        case 9 : c += k[2] & 0xff; b += k[1]; a += k[0]; break;
        case 8 : b += k[1]; a += k[0]; break;
        case 7 : b += k[1] & 0xffffff; a += k[0]; break;
        case 6 : b += k[1] & 0xffff; a += k[0]; break;
        case 5 : b += k[1] & 0xff; a += k[0]; break;
        case 4 : a += k[0]; break;
        case 3 : a += k[0] & 0xffffff; break;
        case 2 : a += k[0] & 0xffff; break;
        case 1 : a += k[0] & 0xff; break;
        case 0 : return c;
    }

    final(a, b, c);

    // Space limit
    //c &= HASH_SPACE_LIMITION;
    
    return c;
}
